/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package extension.game.poker;

import log.Debug;
import com.smartfoxserver.v2.entities.Room;
import com.smartfoxserver.v2.entities.User;
import extension.game.database.Database;
import extension.game.poker.handeval.TexasHoldemHandResult;
import extension.game.zone.GameZoneExtension;
import extension.poker.zone.Flag;
import extension.room.analyse.CardDealer;
import java.util.LinkedList;
import java.util.List;

/**
 *
 * @author daz
 */
public class PokerGame implements Runnable {

    public static final int FOLD = 0;
    public static final int CHECK = 1;
    public static final int CALL = 2;
    public static final int RAISE = 3;
    public static final int BET = 4;
    public static final int PRE_FLOP = 0;
    public static final int FLOP = 1;
    public static final int TURN = 2;
    public static final int RIVER = 3;
    public static final int SHOW_DOWN = 4;
    public static final int HIGH_CARD = 0;
    public static final int ONE_PAIR = 1;
    public static final int TWO_PAIR = 2;
    public static final int THREE_OF_KIND = 3;
    public static final int STRAIGHT = 4;
    public static final int FLUSH = 5;
    public static final int FULL_HOUSE = 6;
    public static final int FOUR_OF_KIND = 7;
    public static final int STRAIGHT_FLUSH = 8;
    public static final int ROYAL_FLUSH = 9;
    //amount money blind bet
    private int amountBlindBet = 10;
    //id of small blind player
    private int smallBlind;
    private int bigBlind;
    public static int MAX_PLAYER_JOIN = 9;
    //id of last dealer
    private int lastDealer;
    //id of dealer;
    private int dealer;
    //amount of money in the pot
    private int potAmount;
    private int numberPlayers;
    //bet turn
    private int bettingTurn;
    //amount money to join game
    private int moneyJoinMin = 10;
    private int moneyJoinMax = 10000;
    private boolean isFinishRound;
    private boolean isFinishMatch;
//    private boolean isFinishOutOfMOney;
    private boolean isFinishByFold;
    private int lastPlayerBet;
    //so player da bet
    private int numberBetted;
    private CardDealer pokerDealer;
    private int currentRound;
    private int matchWinner;
    private boolean isPlay;
    private long moneyMustBet;
    private int timeCount;
    //so player van con bet
    private int numberRemainBet;
    private boolean isRunning;
    private long startTime;
    private int moneyLeave;
    private int numberFinishByFold;
    private boolean isFinishByAllIn;
    private int numberWaiting;
    private int numberAllIn;
    private List<Integer> communityCard;
    private PokerPlayer[] listPlayers;
    private Room room;
    private RoomResponse response;
    private PotManager potManager;
    private PokerFinisher pokerFinisher;
    private List<PokerPlayer> listPlayerEarnMoneys;
    private int countSegment;
    private boolean isSendPotAmount;
    private Database database;
    private PokerRoomExtension pokerRoomExtension;
    private long playerLeaveBetMoney;
//    private long playerLeaveMoney;
    private boolean isReseted;
    public static final int[] listLevelBet = {1, 2, 5, 25, 100, 500, 2000, 5000, 250000, 500000, 1000000};

    public PokerGame(PokerRoomExtension pokerRoomExtension, int roomId) {
        pokerFinisher = new PokerFinisher();
        potManager = new PotManager();
        response = pokerRoomExtension.getResponse();
        room = pokerRoomExtension.getParentRoom();
        communityCard = new LinkedList<Integer>();
        listPlayers = new PokerPlayer[MAX_PLAYER_JOIN];
        listPlayerEarnMoneys = new LinkedList<PokerPlayer>();
        pokerDealer = new CardDealer();
        database = pokerRoomExtension.getDatabase();
        isReseted = true;
        resetRoom();
        int startMoney = listLevelBet[roomId / GameZoneExtension.numberTablePerRooms];
        setRoomBet(startMoney * 2, startMoney * 10, startMoney * 200);

//        if (roomId < 11) {
//            setRoomBet(2, 20, 400);
//        } else if (roomId < 21) {
//            setRoomBet(4, 40, 800);
//        } else if (roomId < 31) {
//            setRoomBet(10, 100, 2000);
//        } else if (roomId < 41) {
//            setRoomBet(50, 500, 10000);
//        } else if (roomId < 51) {
//            setRoomBet(200, 2000, 40000);
//        } else if (roomId < 61) {
//            setRoomBet(1000, 20000, 100000);
//        } else if (roomId < 71) {
//            setRoomBet(4000, 40000, 800000);
//        } else if (roomId < 81) {
//            setRoomBet(100000, 1000000, 20000000);
//        } else if (roomId < 91) {
//            setRoomBet(500000, 5000000, 100000000);
//        } else if (roomId < 101) {
//            setRoomBet(10000000, 10000000, 20000000);
//        } else {
//            setRoomBet(2000000, 100000000, 1000000000);
//        }

    }

    private void setRoomBet(int blindBet, int buyMin, int buyMax) {
        amountBlindBet = blindBet;
        moneyJoinMin = buyMin;
        moneyJoinMax = buyMax;

    }

    public CardDealer getPokerDealer() {
        return pokerDealer;
    }

    public void start() {
        isRunning = true;
//        Thread gameThread = new Thread(this);
//        gameThread.start();
    }

    private void resetRoom() {
        numberPlayers = 0;
        isPlay = false;
        countSegment = -1;
        lastDealer = 0;
        numberAllIn = 0;
    }

    public int addPlayer(PokerPlayer pokerPlayer, int chairId) {
        if (!Debug.isDebug) {
            if (listPlayers[chairId] != null || pokerPlayer.getMoney() < moneyJoinMin) {
                //seat already have player
                return 0;
            }
        }

        int result = 1;
        listPlayers[chairId] = pokerPlayer;
        numberPlayers++;
        // if match is playing
        if (isPlay) {
            pokerPlayer.setState(PokerPlayer.WAITING);
            numberWaiting++;
            result = 2;
        } else {
            startTime = System.currentTimeMillis();
        }
        addTableInfo();
        pokerPlayer.setSeatPos(chairId);
        return result;
    }

    private void sendUpdate(User userExit) {
        String groupName = room.getGroupId();
        Room lobbyRoom = this.pokerRoomExtension.getParentZone().getRoomByName("The Lobby");
        //        List<Room> listRooms = ext.getParentZone().getRoomListFromGroup(groupName);
        List<User> listSend = lobbyRoom.getUserList();
        listSend.remove(userExit);
        for (User u : listSend) {
            Debug.d("send update to :" + u.getName());
            String groupView = (String) u.getProperty(Flag.GROUP_NAME);
            if (groupView.equals(groupName)) {
                response.sendUpdateTable(room.getName(), numberPlayers, room.getGroupId(), u);
            }
        }
    }

    private void addTableInfo() {
        Debug.d("table info ");
        List<String> listPlayerNames = new LinkedList<String>();
        List<Integer> listPlayerPositions = new LinkedList<Integer>();
        for (int i = 0; i < listPlayers.length; i++) {
            if (listPlayers[i] != null) {
                if (listPlayers[i].getState() != PokerPlayer.PLAYING) {
                    listPlayerPositions.add(0);
                } else {
                    listPlayerPositions.add(1);
                }
                listPlayerNames.add(listPlayers[i].getName());
            } else {
                listPlayerPositions.add(-1);
                listPlayerNames.add("0");
            }
        }
        Debug.d("player name :" + listPlayerNames.toString());
        Debug.d("player pos :" + listPlayerPositions.toString());
        room.setProperty(PokerFlag.NUMBER_PLAYERS, numberPlayers);
        room.setProperty(PokerFlag.ROOM_PLAYER_NAME, listPlayerNames);
        room.setProperty(PokerFlag.ROOM_PLAYER_POS, listPlayerPositions);
        Room lobbyRoom = room.getExtension().getParentZone().getRoomByName("The Lobby");
        response.sendTableInfo(room.getName(), listPlayerNames, listPlayerPositions, lobbyRoom.getUserList());
    }

    private void findDealer() {
        for (int i = lastDealer + 1; i < lastDealer + MAX_PLAYER_JOIN; i++) {
            if (listPlayers[i % MAX_PLAYER_JOIN] != null && listPlayers[i % MAX_PLAYER_JOIN].getState() == PokerPlayer.PLAYING) {
                dealer = i % MAX_PLAYER_JOIN;
                lastDealer = dealer;
                break;
            }
        }
    }

    private void findSmallBlind() {
        if (numberPlayers == 2) {
            smallBlind = dealer;
            return;
        }
        for (int i = dealer + 1; i < dealer + MAX_PLAYER_JOIN; i++) {
            if (listPlayers[i % MAX_PLAYER_JOIN] != null && listPlayers[i % MAX_PLAYER_JOIN].getState() == PokerPlayer.PLAYING) {
                smallBlind = i % MAX_PLAYER_JOIN;
                break;
            }
        }
    }

    private void findBigBlind() {
        for (int i = smallBlind + 1; i < smallBlind + MAX_PLAYER_JOIN; i++) {
            if (listPlayers[i % MAX_PLAYER_JOIN] != null && listPlayers[i % MAX_PLAYER_JOIN].getState() == PokerPlayer.PLAYING) {
                bigBlind = i % MAX_PLAYER_JOIN;
                break;
            }
        }
    }

    private void resetWaiting() {
        numberPlayers = 0;
        List<Integer> listSend = new LinkedList<Integer>();
        for (int i = 0; i < MAX_PLAYER_JOIN; i++) {
            if (listPlayers[i] != null) {
                listPlayers[i].setBettingChoice(-1);
                listPlayers[i].setBet(0);
                numberPlayers++;
                if (listPlayers[i].getState() == PokerPlayer.WAITING) {
                    listPlayers[i].setState(PokerPlayer.PLAYING);
                    listSend.add(i);
                }
            }
        }
        if (!listSend.isEmpty()) {
            response.sendListWaiting(listSend);
        }
    }

    private void initMatch() {
        resetWaiting();
        numberWaiting = 0;
        currentRound = 0;
        moneyMustBet = 0;
        potAmount = 0;
//        isFinishOutOfMOney = false;
        isFinishMatch = false;
        isPlay = false;
        communityCard.clear();
        moneyMustBet = 0;
        numberBetted = 0;
        isFinishRound = false;
        isFinishByFold = false;
        isFinishByAllIn = false;
        moneyLeave = 0;
        numberFinishByFold = 0;
        numberAllIn = 0;
    }

    public void startNewMatch() {
        initMatch();
        isPlay = true;
        //find player out of money
        LinkedList<Integer> listOutOfMoney = new LinkedList<Integer>();
        LinkedList<Long> listPlayerMoney = new LinkedList<Long>();
        numberWaiting = 0;
        for (int i = 0; i < MAX_PLAYER_JOIN; i++) {
            if (listPlayers[i] != null) {
                if (listPlayers[i].getMoney() < amountBlindBet) {
                    listOutOfMoney.add(i);
                    long moneyRemain = listPlayers[i].getMoney();
                    long newMoney = listPlayers[i].getTotalMoney() + moneyRemain;
                    this.database.updateUserMoney(listPlayers[i].getName(), newMoney);
                    this.database.deleteMoneyQueue("R", listPlayers[i].getName());
                    listPlayerMoney.add(newMoney);
                } else if (listPlayers[i].getState() != PokerPlayer.PLAYING) {
                    numberWaiting++;
                }
            }
        }
        numberPlayers -= (listOutOfMoney.size() + numberWaiting);
        boolean isContinue = (numberPlayers <= 1 ? false : true);
        if (!listOutOfMoney.isEmpty()) {
            response.sendListOutOfMoney(listOutOfMoney, listPlayerMoney, isContinue);
            for (Integer userId : listOutOfMoney) {
                if (listPlayers[userId] != null) {
                    response.sendMessageChat(listPlayers[userId].getName() + " hết tiền", 0);
                    listPlayers[userId] = null;
                }
            }
            addTableInfo();
        }

        if (isContinue) {
            isReseted = false;
            numberRemainBet = numberPlayers - numberWaiting;
            //find role
            findDealer();
            findSmallBlind();
            findBigBlind();
            // set blind bet
            listPlayers[smallBlind].setBet(amountBlindBet / 2);
            listPlayers[bigBlind].setBet(amountBlindBet);
            potManager.betAddPot(smallBlind, amountBlindBet / 2);
            potManager.betAddPot(bigBlind, amountBlindBet);
            moneyMustBet = amountBlindBet;
            // send player role
            response.sendPlayerRoles(dealer, smallBlind, bigBlind, amountBlindBet, listPlayers[smallBlind].getMoney(), listPlayers[bigBlind].getMoney(), room.getUserList());
            //find betting turn

            for (int i = bigBlind + 1; i < MAX_PLAYER_JOIN + bigBlind; i++) {
                if (listPlayers[i % MAX_PLAYER_JOIN] != null && listPlayers[i % MAX_PLAYER_JOIN].getState() == PokerPlayer.PLAYING) {
                    bettingTurn = i % MAX_PLAYER_JOIN;
                    break;
                }
            }
            // use for start loop find next turn
            lastPlayerBet = bettingTurn;
            //shuffle card
            pokerDealer.shuffleCard();
            for (int i = 0; i < MAX_PLAYER_JOIN; i++) {
                if (listPlayers[i] != null) {
                    if (listPlayers[i].getState() == PokerPlayer.PLAYING) {
                        listPlayers[i].card1 = pokerDealer.nextCard();
                        listPlayers[i].card2 = pokerDealer.nextCard();
                        response.sendDealCard(listPlayers[i].card1, listPlayers[i].card2, listPlayers[i].getUser());
                    }
                }
            }
            response.sendMessageChat("Dealer chia bài", 0);
            //send bet turn
            sendBettingTurn(1);
            response.sendMessageChat("Vòng PRE FLOP", 0);
            currentRound = PRE_FLOP;
            logMatchStart();
        } else {
            isPlay = false;
        }
    }

    private void logMatchStart() {
        Debug.line("start match");
        for (int i = 0; i < MAX_PLAYER_JOIN; i++) {
            if (listPlayers[i] != null && listPlayers[i].getState() == PokerPlayer.PLAYING) {
                Debug.i(listPlayers[i].getName() + "-M=" + listPlayers[i].getMoney() + ",B=" + listPlayers[i].getBet() + ",S=" + listPlayers[i].getState());
                Debug.i(listPlayers[i].getName() + "-Card:" + PokerConversion.getCardName(listPlayers[i].card1) + "," + PokerConversion.getCardName(listPlayers[i].card2));
            }
        }
        Debug.i("match start bet :" + listPlayers[bettingTurn].getName());
    }

    private void sendBettingTurn(int bettingOption) {
        //betting option 0: fold check raise, 1: fold call raise, 2: fold call
        if (listPlayers[bettingTurn].getMoney() + listPlayers[bettingTurn].getBet() > moneyMustBet) {
            //if enought money to raise. 2 case: win all and win 1 part
            if (moneyMustBet > 0) {
                long raiseMin =
                        (listPlayers[bettingTurn].getMoney() + listPlayers[bettingTurn].getBet() > moneyMustBet * 2
                        ? moneyMustBet * 2 - listPlayers[bettingTurn].getBet()
                        : listPlayers[bettingTurn].getMoney());
                long raiseMax = 0;
                long maxMoney = 0;
                //find max money beside player bet turn
                for (int i = bettingTurn + 1; i < bettingTurn + MAX_PLAYER_JOIN; i++) {
                    int idCheck = i % MAX_PLAYER_JOIN;
                    if (listPlayers[idCheck] != null && idCheck != bettingTurn
                            && listPlayers[idCheck].getState() == PokerPlayer.PLAYING) {
                        long moneyCheck = listPlayers[idCheck].getMoney() + listPlayers[idCheck].getBet() - listPlayers[bettingTurn].getBet();
                        if (moneyCheck >= maxMoney) {
                            //so tien can bo ra de raise het tien 1 thang
                            maxMoney = moneyCheck;
                        }
                    }
                }

                raiseMax = (listPlayers[bettingTurn].getMoney() >= maxMoney ? maxMoney : listPlayers[bettingTurn].getMoney());
                raiseMin = (raiseMin >= raiseMax ? raiseMax : raiseMin);
                response.sendBettingTurn(bettingTurn, bettingOption, raiseMin, raiseMax, room.getUserList());

            } else {
                //luc chua ai dat cuoc
                if (listPlayers[bettingTurn].getMoney() <= amountBlindBet) {
                    long raiseMin = listPlayers[bettingTurn].getMoney();
                    long raiseMax = listPlayers[bettingTurn].getMoney();
                    response.sendBettingTurn(bettingTurn, 0, raiseMin, raiseMax, room.getUserList());
                } else {
                    long raiseMin = amountBlindBet;
                    long maxMoney = 0;
                    //find max money beside player bet turn
                    for (int i = bettingTurn + 1; i < bettingTurn + MAX_PLAYER_JOIN; i++) {
                        if (listPlayers[i % MAX_PLAYER_JOIN] != null) {
                            if (listPlayers[i % MAX_PLAYER_JOIN].getMoney() >= maxMoney) {
                                //so tien can bo ra de raise het tien 1 thang
                                maxMoney = listPlayers[i % MAX_PLAYER_JOIN].getMoney() + listPlayers[i % MAX_PLAYER_JOIN].getBet() - listPlayers[bettingTurn].getBet();
                            }
                        }
                    }
                    long raiseMax = (listPlayers[bettingTurn].getMoney() >= maxMoney ? maxMoney : listPlayers[bettingTurn].getMoney());
                    raiseMin = (raiseMin >= raiseMax ? raiseMax : raiseMin);
                    response.sendBettingTurn(bettingTurn, 0, raiseMin, raiseMax, room.getUserList());
                }
            }
        } else {
            response.sendBettingTurn(bettingTurn, 2, 0, 0, room.getUserList());
        }
    }

    public void nextTurn() {
        if (!isFinishMatch) {
            bettingTurn = -1;
            for (int i = lastPlayerBet + 1; i < lastPlayerBet + MAX_PLAYER_JOIN; i++) {
                int idCheck = i % MAX_PLAYER_JOIN;
                if (listPlayers[idCheck] != null) {
                    //chon nhung thang khong fold va khong cho, chua het tien
                    if (listPlayers[idCheck].getBettingChoice() != FOLD && listPlayers[idCheck].getState() == PokerPlayer.PLAYING
                            && listPlayers[idCheck].getMoney() > 0) {
                        bettingTurn = idCheck;
                        lastPlayerBet = bettingTurn;
                        break;
                    }
                }
            }

            checkFinishRound();

            if (bettingTurn > -1 && !isFinishRound) {
                response.sendMessageChat("Đến lượt " + listPlayers[bettingTurn].getName(), 0);
            }

            if (bettingTurn == -1 || isFinishByAllIn || (numberRemainBet - numberAllIn <= 1 && isFinishRound)) {
                betAllMoney();
                return;
            }

            if (!isFinishRound) {
                //betting option 0: fold check raise, 1: fold call raise, 2: fold call
                int bettingOption = (listPlayers[bettingTurn].getBet() >= moneyMustBet ? 0 : 1);
                if (numberRemainBet - numberAllIn == 1) {
                    bettingOption = 2;
                }
                sendBettingTurn(bettingOption);
            } else {
                startTime = System.currentTimeMillis();
                isSendPotAmount = true;
            }

        } else {
            isFinishRound = false;
            isSendPotAmount = true;
            startTime = System.currentTimeMillis();
        }
    }

    private void sendCardHint() {
        for (int i = 0; i < MAX_PLAYER_JOIN; i++) {
            if (listPlayers[i] != null && listPlayers[i].getBettingChoice() != FOLD && listPlayers[i].getState() == PokerPlayer.PLAYING) {
                int numberOpenCard = communityCard.size();
                int[] hand = new int[numberOpenCard + 2];
                for (int j = 0; j < numberOpenCard; j++) {
                    hand[j] = communityCard.get(j);
                }
                hand[numberOpenCard] = listPlayers[i].card1;
                hand[numberOpenCard + 1] = listPlayers[i].card2;
                Debug.d("name :" + listPlayers[i].getName());
                Debug.d("hand size :" + hand.length);
                TexasHoldemHandResult handEval = new TexasHoldemHandResult(hand);
                listPlayers[i].handRank = handEval.rank;
                listPlayers[i].setHandStrength(handEval.type);
                int[] handRes = new int[7];
                for (int j = 0; j < handEval.numOfSignificantCards; j++) {
                    if (handEval.highest5Card[j] == numberOpenCard + 1) {
                        Debug.d("card hint :" + PokerConversion.getCardName(hand[numberOpenCard + 1]));
                        handRes[6] = 1;
                        if (handEval.type == HIGH_CARD) {
                            break;
                        }
                    } else if (handEval.highest5Card[j] == numberOpenCard) {
                        Debug.d("card hint :" + PokerConversion.getCardName(hand[numberOpenCard]));
                        handRes[5] = 1;
                        if (handEval.type == HIGH_CARD) {
                            break;
                        }
                    } else {
                        Debug.d("card hint :" + PokerConversion.getCardName(hand[handEval.highest5Card[j]]));
                        if (handEval.type > HIGH_CARD) {
                            handRes[handEval.highest5Card[j]] = 1;
                        }
                    }
                }

                listPlayers[i].cardArr = handRes;

                if (handEval.type > HIGH_CARD) {
                    response.sendCardHint(handRes, handEval.type, listPlayers[i].getUser());
                }
            }
        }
    }

    private void sendPotAmount() {
        numberRemainBet = 0;
        for (int i = 0; i < MAX_PLAYER_JOIN; i++) {
            if (listPlayers[i] != null) {
                Debug.d("remain :" + listPlayers[i].getName() + ", bet :" + listPlayers[i].getBet());
                potAmount += listPlayers[i].getBet();
                listPlayers[i].setBet(0);
                if (listPlayers[i].getState() == PokerPlayer.PLAYING && listPlayers[i].getMoney() > 0 && listPlayers[i].getBettingChoice() != FOLD) {
                    numberRemainBet++;
                }
            }
        }
        Debug.d("money leave :" + moneyLeave);
        Debug.d("pot amount:" + potAmount);
        Debug.d("number remain bet :" + numberRemainBet);
        potAmount += moneyLeave;

        moneyLeave = 0;
        response.sendPotAmount(potAmount, room.getUserList());
        isSendPotAmount = false;
        startTime = System.currentTimeMillis();
    }

    public void onFinishRound() {
        Debug.i("on finish round");
        isFinishRound = false;
        isSendPotAmount = false;
        checkFinishMatch();
        if (isFinishMatch) {
            isSendPotAmount = true;
            startTime = System.currentTimeMillis();
//            onFinishMatch();
            return;
        }
        numberBetted = 0;
        currentRound++;
        //community card
        if (currentRound == FLOP) {
            communityCard.add(pokerDealer.nextCard());
            communityCard.add(pokerDealer.nextCard());
            communityCard.add(pokerDealer.nextCard());
            response.sendNewRoundInfo(currentRound, communityCard, room.getUserList());
        } else {
            int newCommunityCard = pokerDealer.nextCard();
            communityCard.add(newCommunityCard);
            response.sendNewRoundInfo(currentRound, newCommunityCard, room.getUserList());
        }

        // when show down  send betting turn
        if (currentRound != SHOW_DOWN) {
            bettingTurn = smallBlind;
            lastPlayerBet = smallBlind;
            if (listPlayers[bettingTurn] == null) {
                for (int i = smallBlind + 1; i < smallBlind + MAX_PLAYER_JOIN; i++) {
                    int idCheck = i % MAX_PLAYER_JOIN;
                    if (listPlayers[idCheck] != null) {
                        if (listPlayers[idCheck].getState() == PokerPlayer.PLAYING && listPlayers[idCheck].getMoney() > 0 && listPlayers[idCheck].getBettingChoice() != FOLD) {
                            bettingTurn = idCheck;
                            lastPlayerBet = idCheck;
                            break;
                        }
                    }
                }
            } else if (listPlayers[bettingTurn].getMoney() <= 0 || listPlayers[bettingTurn].getBettingChoice() == FOLD || listPlayers[bettingTurn].getState() != PokerPlayer.PLAYING) {
                for (int i = smallBlind + 1; i < smallBlind + MAX_PLAYER_JOIN; i++) {
                    int idCheck = i % MAX_PLAYER_JOIN;
                    if (listPlayers[idCheck] != null) {
                        if (listPlayers[idCheck].getState() == PokerPlayer.PLAYING && listPlayers[idCheck].getMoney() > 0 && listPlayers[idCheck].getBettingChoice() != FOLD) {
                            bettingTurn = idCheck;
                            lastPlayerBet = idCheck;
                            break;
                        }
                    }
                }
            }
            //reset money bet on table
            moneyMustBet = 0;
            sendBettingTurn(0);
            logNewRound();
            response.sendMessageChat("Vòng " + PokerConversion.getRoundName(currentRound), 0);
            response.sendMessageChat("Đến lượt " + listPlayers[bettingTurn].getName(), 0);
            sendCardHint();
        }
    }

    private void logNewRound() {

        Debug.line("New round");
        Debug.i("round :" + PokerConversion.getRoundName(currentRound));
        Debug.i("community card :" + PokerConversion.getCardList(communityCard));

    }

    private void betAllMoney() {
        int numberOpenCard = communityCard.size();
        currentRound = RIVER;
        if (numberOpenCard >= 3) {
            for (int i = 0; i < 5 - numberOpenCard; i++) {
                int cardId = pokerDealer.nextCard();
                communityCard.add(cardId);
                response.sendNewRoundInfo(currentRound, cardId, room.getUserList());
            }
        } else {
            communityCard.add(pokerDealer.nextCard());
            communityCard.add(pokerDealer.nextCard());
            communityCard.add(pokerDealer.nextCard());
            communityCard.add(pokerDealer.nextCard());
            communityCard.add(pokerDealer.nextCard());
            response.sendNewRoundInfo(currentRound, communityCard, room.getUserList());
        }
        sendCardHint();
        isFinishMatch = true;
        isSendPotAmount = true;
        startTime = System.currentTimeMillis();
//        onFinishMatch();
    }

    private void onFinishMatch() {
        isFinishMatch = false;
        findWinner();
//        //send result
        if (isFinishByFold && matchWinner >= 0) {
            if (listPlayers[matchWinner] != null) {
                listPlayers[matchWinner].winMoney(potAmount);
                response.sendMatchResult(matchWinner, null, listPlayers[matchWinner].getHandStrength(), potAmount, room.getUserList(), isFinishByFold, null, -1, 1, listPlayers[matchWinner].getMoney());
                response.sendMessageChat(listPlayers[matchWinner].getName() + " thắng " + potAmount, 0);
            }
            startTime = System.currentTimeMillis();
            countSegment = -1;
            isPlay = false;
            logFinishMatch();
            potManager.reset();
            updateMoney();
        } else {
            countSegment = 0;
        }
        Debug.d("countsegment :" + countSegment);
        Debug.d("match winner :" + matchWinner);
    }

    private void sendMatchWinner(int index) {
        Debug.d("send match winner :" + index);
        if (index > listPlayerEarnMoneys.size() - 1) {
            countSegment = -1;
            return;
        }
        PokerPlayer p = listPlayerEarnMoneys.get(index);
        List<Integer> cardWin = new LinkedList<Integer>();
        cardWin.add(p.card1);
        cardWin.add(p.card2);
        List<Integer> cardArr = new LinkedList<Integer>();
        for (int i = 0; i < 7; i++) {
            cardArr.add(p.cardArr[i]);
        }
        int segment = (countSegment == listPlayerEarnMoneys.size() - 1 ? -1 : countSegment);
        long addMoney = p.getAddMoney();
        p.winMoney(addMoney);
        response.sendMatchResult(p.getSeatPos(), cardWin, p.getHandStrength(), addMoney, room.getUserList(), isFinishByFold, cardArr, segment,
                listPlayerEarnMoneys.size(), p.getMoney());
        countSegment++;
        if (segment == -1) {
            logFinishMatch();
            potManager.reset();
            countSegment = -1;
            isPlay = false;
            updateMoney();
        }
        startTime = System.currentTimeMillis();
    }

    private void logFinishMatch() {
        Debug.line("FINISH MATCH");
        if (!isFinishByFold) {
            Debug.i("community card :" + PokerConversion.getCardList(communityCard));
        }

        Debug.d("match winner id :" + matchWinner);
        if (matchWinner >= 0) {
            if (listPlayers[matchWinner] != null) {
                Debug.i("winner :" + listPlayers[matchWinner].getName());
                Debug.i("money win :" + potAmount);
            }
            return;
        }
//        for (PokerPlayer p : listPlayerEarnMoneys) {
//            Debug.i("winner :" + p.getName() + "- type :" + p.getHandStrength() + "-money win :" + p.getAddMoney());
//        }

    }

    private void findWinner() {
        matchWinner = -1;
        int count = 0;
        for (int i = 0; i < MAX_PLAYER_JOIN; i++) {
            if (listPlayers[i] != null) {
                if (listPlayers[i].getBettingChoice() != FOLD && listPlayers[i].getState() == PokerPlayer.PLAYING) {
                    if (!isFinishByFold) {
                        count++;
                    } else {
                        matchWinner = i;
                        return;
                    }
                }
            }
        }
        if (count > 0) {
            PokerPlayer[] listRemains = new PokerPlayer[count];
            int countAdd = 0;
            for (int i = 0; i < MAX_PLAYER_JOIN; i++) {
                if (listPlayers[i] != null) {
                    if (listPlayers[i].getBettingChoice() != FOLD && listPlayers[i].getState() == PokerPlayer.PLAYING) {
                        listRemains[countAdd] = listPlayers[i];
                        countAdd++;
                    }
                }
            }
            int startIndex = 0;
            listPlayerEarnMoneys.clear();
            pokerFinisher.generateWinner(listRemains);
            List<PokerPlayer> listWinner;
            do {
                listWinner = pokerFinisher.getListWinners(startIndex);
                int numberWinners = potManager.generateMoneyWin(listWinner);
                listPlayerEarnMoneys.addAll(listWinner);
                startIndex += numberWinners;
                Debug.d("start index :" + startIndex);
            } while (startIndex < count && potManager.isContinueSplit() && !listWinner.isEmpty());
        }
    }

    private void checkFinishRound() {
        Debug.d("numberBetted:" + numberBetted);
        Debug.d("numberRemainBet:" + numberRemainBet);

        if (numberBetted >= numberRemainBet) {
            isFinishRound = true;
        }
    }

    private void checkFinishMatch() {
        if (currentRound == RIVER) {
            currentRound = SHOW_DOWN;
            isFinishMatch = true;
        }
    }

    private boolean checkTimeDelay(long delay) {
//        if (room.getName().equals("5")) {
//            Debug.d("isPlay:" + isPlay + ",count segment :" + countSegment);
//            Debug.d("check delay:" + delay);
//            Debug.d("check time:" + (System.currentTimeMillis() - startTime));
//        }
        return (delay < System.currentTimeMillis() - startTime);
    }

    @Override
    public void run() {
        if (isRunning) {
            if (isFinishRound) {
                if (isSendPotAmount) {
                    sendPotAmount();
                } else {
                    onFinishRound();
                }
            }

            if (isFinishMatch) {
                if (isSendPotAmount) {
                    sendPotAmount();
                }
                if (!isSendPotAmount && checkTimeDelay(1000)) {
                    onFinishMatch();
                }
            }

            if (countSegment > -1) {
                if (countSegment == 0) {
                    sendMatchWinner(countSegment);
                }
                if (countSegment > 0) {
                    sendMatchWinner(countSegment);
                }
            }

            if (!isPlay && countSegment == -1) {
                if (!isReseted) {
                    if (checkTimeDelay(5000)) {
                        startNewMatch();
                    }
                } else {
                    startNewMatch();
                }

            }

        }
    }

    public long getPlayerLeaveBetMoney() {
        return playerLeaveBetMoney;
    }

    public long removePlayer(User user) {
        long playerMoney = 0;
        for (int i = 0; i < listPlayers.length; i++) {
            if (listPlayers[i] != null) {
                if (listPlayers[i].getName().equals(user.getName())) {
//                    playerLeaveMoney = listPlayers[i].getTotalMoney();
                    playerMoney = listPlayers[i].getMoney() + listPlayers[i].getTotalMoney();
                    playerLeaveBetMoney = listPlayers[i].getBet();
                    moneyLeave += listPlayers[i].getBet();
                    listPlayers[i] = null;
                    numberPlayers--;
                    if (isPlay) {
                        numberRemainBet--;
                    }
                    break;
                }
            }
        }
        addTableInfo();
        if (numberPlayers <= 0) {
            resetRoom();
            isFinishMatch = true;
            isPlay = false;
        } else if (numberPlayers < 2) {
            if (isPlay) {
                isFinishMatch = true;
                isFinishByFold = true;
                //reset bet
                isSendPotAmount = true;
                startTime = System.currentTimeMillis();
            }
        }
        if (numberPlayers <= 1) {
            isReseted = true;
        }
        return playerMoney;
    }

//    public long getPlayerLeaveMoney() {
//        return playerLeaveMoney;
//    }
    public long getMoneyMustBet() {
        return moneyMustBet;
    }

    public void setMustBet(long bettingMoney) {
        moneyMustBet = bettingMoney;
        numberBetted = 1;
    }

    public void checkFinishWhenFold(int bettingPlayer) {
        Debug.line("check finish when fold");
        numberRemainBet--;
        numberBetted--;
        numberFinishByFold++;

        if (numberFinishByFold >= numberPlayers - numberWaiting - 1) {
            //fold het con moi mot thang
            isFinishByFold = true;
            isFinishMatch = true;
            isFinishRound = true;
        } else if (numberRemainBet <= 1) {
            isFinishByAllIn = true;
        }
        Debug.d("isFinishAllIn :" + isFinishByAllIn);
        Debug.d("isFinishFold :" + isFinishByFold);
    }

    public void betAction() {
        numberBetted++;
    }

    public void checkFinishWhenAllBet(int bettingPlayer) {
        numberAllIn++;

    }

    public void sitOut(User user) {
        for (int i = 0; i < MAX_PLAYER_JOIN; i++) {
            if (listPlayers[i] != null) {
                if (listPlayers[i].getUser().getId() == user.getId()) {
                    listPlayers[i].setState(PokerPlayer.SIT_OUT);
                    if (isPlay) {
                        listPlayers[i].setBettingChoice(PokerGame.FOLD);
                        checkFinishWhenFold(i);
                        if (isFinishByFold && bettingTurn != i) {
                            nextTurn();
                        }
                    }
                    break;
                }
            }
        }


    }

    public void finishSitOut(User user) {
        for (int i = 0; i < MAX_PLAYER_JOIN; i++) {
            if (listPlayers[i] != null) {
                if (listPlayers[i].getUser().getId() == user.getId()) {
                    listPlayers[i].setState(PokerPlayer.WAITING);
                    break;
                }
            }
        }
    }
    /* GETTERS & SETTERS */

    public void setBettingTurn(int bettingTurn) {
        this.bettingTurn = bettingTurn;
    }

    public int getBettingTurn() {
        return bettingTurn;
    }

    public void setCommunityCard(List<Integer> communityCard) {
        this.communityCard = communityCard;
    }

    public List<Integer> getCommunityCard() {
        return communityCard;
    }

    public void setListPlayers(PokerPlayer[] listPlayers) {
        this.listPlayers = listPlayers;
    }

    public PokerPlayer[] getListPlayers() {
        return listPlayers;
    }

    public void setMaxPlayerJoin(int maxPlayerJoin) {
        PokerGame.MAX_PLAYER_JOIN = maxPlayerJoin;
    }

    public int getMaxPlayerJoin() {
        return MAX_PLAYER_JOIN;
    }

    public void setMoneyJoinMax(int moneyJoinMax) {
        this.moneyJoinMax = moneyJoinMax;
    }

    public int getMoneyJoinMax() {
        return moneyJoinMax;
    }

    public void setMoneyJoinMin(int moneyJoinMin) {
        this.moneyJoinMin = moneyJoinMin;
    }

    public int getMoneyJoinMin() {
        return moneyJoinMin;
    }

    public void setPotAmount(int potAmount) {
        this.potAmount = potAmount;
    }

    public int getPotAmount() {
        return potAmount;
    }

    public void setIsPlay(boolean isPlay) {
        this.isPlay = isPlay;
    }

    public boolean getIsPlay() {
        return isPlay;
    }

    public boolean getIsFinish() {
        return isFinishMatch;
    }

    public int getNumberPlayers() {
        return numberPlayers;
    }

    public void contributePot(long bettingMoney, int playerId) {
        potManager.betAddPot(playerId, bettingMoney);
    }

    private void updateMoney() {
        List<Long> listMoneys = new LinkedList<Long>();
        for (int i = 0; i < MAX_PLAYER_JOIN; i++) {
            if (listPlayers[i] != null) {
                listMoneys.add(listPlayers[i].getMoney());
            } else {
                listMoneys.add(0l);
            }
        }

        for (int i = 0; i < MAX_PLAYER_JOIN; i++) {
            if (listPlayers[i] != null) {
                Debug.d("update " + listPlayers[i].getName() + ",bet :" + listPlayers[i].getMoney() + ",total:" + listPlayers[i].getTotalMoney());
                if (!Debug.isDebug) {
                    database.updateMoneyJoin("R", listPlayers[i].getName(), listPlayers[i].getMoney());
                }
                response.sendUpdateMoney(listPlayers[i], listMoneys);
            }
        }

    }
}
